import { trim, isEmpty } from 'lodash';
import { Op, Model, IncludeOptions } from 'sequelize';
import { validationResult } from 'express-validator';
import { getSequelize } from '../db';
import ExpressValidationError from '../utils/validation-error';
import { getOptions, getModelDifference } from '../utils/sql-helper';

export default class BaseController {
  constructor() {
    this.getFindWhere = this.getFindWhere.bind(this);
    this.getInclude = this.getInclude.bind(this);
    this.getModel = this.getModel.bind(this);
    this.findAll = this.findAll.bind(this);
    this.postFindAll = this.postFindAll.bind(this);
    this.findOne = this.findOne.bind(this);
    this.create = this.create.bind(this);
    this.bulkCreate = this.bulkCreate.bind(this);
    this.update = this.update.bind(this);
    this.afterUpdate = this.afterUpdate.bind(this);
    this.destroy = this.destroy.bind(this);
    this.formatter = this.formatter.bind(this);
  }

  /**
   * 解析查询条件
   * @returns {Object}
   */
  getFindWhere(req) {
    const { code, name, disabled } = req.query;
    const where = {};
    const attributes = this.getModel().getAttributes();
    if (attributes.hotel_id) {
      where.hotel_id = req.user.hotel_id;
    }

    if (trim(code)) {
      where.code = { [Op.like]: `%${trim(code)}%` };
    }

    if (trim(name)) {
      where.name = { [Op.like]: `%${trim(name)}%` };
    }

    if (disabled === 'Y') {
      where.disabled = true;
    }
    else if (disabled === 'N') {
      where.disabled = false;
    }

    return { where: isEmpty(where) ? undefined : where };
  }

  /**
   * 获取Model
   * @returns {Model}
   */
  getModel() {
    return {};
  }

  /**
   * 获取associations
   * @returns {Object.<string, Array<IncludeOptions>>}
   */
  getInclude() {
    return {};
  }

  customId() {
    return false;
  }

  async postFindAll(data) {
    return data;
  }

  async findAll(req, res, next) {
    try {
      const options = getOptions(req, this.getFindWhere, this.getInclude);
      const model = this.getModel();
      if (options.pageIndex < 0) {
        const rows = await model.findAll(options);
        res.sendOk(await this.postFindAll({ rows }));
      }
      else {
        const data = await model.findAndCountAll({ ...options, distinct: true });
        res.sendOk(await this.postFindAll(data));
      }
    }
    catch (e) {
      next(e);
    }
  }

  async findOne(req, res, next) {
    try {
      const { id } = req.params;
      const model = this.getModel();
      const data = await model.findOne({ where: { id }, rejectOnEmpty: new Error('数据不存在') });
      res.sendOk(data);
    }
    catch (e) {
      next(e);
    }
  }

  formatter(req) {
    if (!this.customId()) {
      delete req.body.id;
    }
    delete req.body.version;
    delete req.body.created_at;
    delete req.body.updated_at;

    req.body.created_id = req.user.id;
    req.body.updated_id = req.user.id;

    const model = this.getModel();
    const attributes = model.getAttributes();
    if (attributes.hotel_id) {
      req.body.hotel_id = req.user.hotel_id;
    }
  }

  async create(req, res, next) {
    try {
      const result = validationResult(req);
      if (!result.isEmpty()) {
        throw new ExpressValidationError({ errors: result.array() });
      }

      this.formatter(req);
      const model = this.getModel();

      const transaction = await getSequelize().transaction();
      req.transaction = transaction;
      const data = await model.create(req.body, { req, transaction });
      await transaction.commit();
      res.sendOk(data);
    }
    catch (e) {
      next(e);
    }
  }

  async bulkCreate(req, res, next) {
    try {
      const model = this.getModel();
      if (Array.isArray(req.body)) {
        for (const row of req.body) {
          row.hotel_id = req.user.hotel_id;
        }
        const transaction = await getSequelize().transaction();
        req.transaction = transaction;
        const result = await model.bulkCreate(req.body, { updateOnDuplicate: ['name', 'disabled', 'sequence'], transaction });
        await transaction.commit();
        res.sendOk(result);
      }
      else {
        throw new Error('数据格式错误');
      }
    }
    catch (e) {
      next(e);
    }
  }

  async afterUpdate(req, instance) {
    return true;
  }

  async update(req, res, next) {
    try {
      const result = validationResult(req);
      if (!result.isEmpty()) {
        throw new ExpressValidationError({ errors: result.array() });
      }

      const { id } = req.params;
      const { body } = req;
      const db = getSequelize();
      const model = this.getModel();
      const transaction = await db.transaction();
      req.transaction = transaction;
      const instance = await model.findOne({ where: { id }, transaction, rejectOnEmpty: new Error('数据不存在') });
      const attributes = model.getAttributes();

      for (const [key, attribute] of Object.entries(attributes)) {
        // console.log(attribute);
        if (attribute.editable !== false) {
          instance.setDataValue(key, body[key]);
        }
      }
      instance.setDataValue('updated_id', req.user.id);

      const logs = await getModelDifference(model, instance);
      console.log(logs);
      const data = await instance.save({ transaction, req });

      await this.afterUpdate(req, instance);
      await transaction.commit();
      res.sendOk(data);
    }
    catch (e) {
      next(e);
    }
  }

  async destroy(req, res, next) {
    try {
      const { id } = req.params;
      const db = getSequelize();
      const transaction = await db.transaction();
      req.transaction = transaction;

      const model = this.getModel();
      const hotel = await model.findOne({ where: { id }, transaction, rejectOnEmpty: new Error('数据不存在') });

      const data = await hotel.destroy({ transaction, req });
      await transaction.commit();
      res.sendOk(data);
    }
    catch (e) {
      next(e);
    }
  }
}
